<script setup>
import { ref, computed, watch, onMounted } from 'vue'
import { Delete, Plus, ZoomIn, Document, Download } from '@element-plus/icons-vue'
import COS from 'cos-js-sdk-v5'
import dayjs from 'dayjs'
import { ElMessage } from 'element-plus'
import Sortable from 'sortablejs'
import api from "/@/api/common";

const props = defineProps({
  modelValue: {
    type: Array,
    default: () => [],
  },
  accept: {
    type: String,
    default: 'image/*',
  },
  fileType: {
    type: Array,
    default: () => ['jpg', 'jpeg', 'png', 'gif', 'webp', 'avif'],
  },
  limit: {
    type: Number,
    default: 50,
  },
  maxSize: {
    type: Number,
    default: 20,
  },
  aspectRatio: {
    // 图片宽高比
    type: Number,
  },
  aspectRatioStr: {
    // 图片宽高比
    type: String,
  },
  isShowTip: {
    type: Boolean,
    default: true,
  },
  disabled: {
    type: Boolean,
    default: false,
  },
})
// 上传的图片服务器地址
const emit = defineEmits(['update:modelValue'])
const dialogImageUrl = ref('')
const dialogVisible = ref(false)
const imageUploadRef = ref()
const fileListRef = ref()
const number = ref(0)
const uploadList = ref([]) // 上传图片列表
const fileList = ref([])
const showTip = computed(() => props.isShowTip && (props.fileType.length || props.maxSize || props.aspectRatio))
// 初始化oss
const cosObj = new COS({
  Protocol: 'https:', // 使用 https 协议
  async getAuthorization(options, callback) {
    const { data } = await api.getCredential()
    callback({
      TmpSecretId: data.tmpSecretId,
      TmpSecretKey: data.tmpSecretKey,
      SecurityToken: data.sessionToken,
      // 建议返回服务器时间作为签名的开始时间，避免客户端本地时间偏差过大导致签名错误
      StartTime: data.startTime, // 时间戳，单位秒，如：1580000000
      ExpiredTime: data.expiredTime, // 时间戳，单位秒，如：1580000000
      ScopeLimit: true, // 细粒度控制权限需要设为 true，会限制密钥只在相同请求时重复使用
    })
  },
})
// 监听父组件的值
watch(
  () => props.modelValue,
  () => {
    fileList.value = props.modelValue.map((m) => {
      if (typeof m === 'string') {
        return {
          name: m,
          url: m,
        }
      }
      else {
        return m
      }
    })
  },
  { immediate: true },
)

// 判断是否是图片
function isValidImageUrl(url) {
    const regex = /(http|https):\/\/.*\.(png|jpg|jpeg|gif|bmp|svg)$/i;
    return regex.test(url);
}

async function handleBeforeUpload(file) {
  const { type, size } = file
  const { fileType, maxSize } = props
  if (fileType.length && !fileType.includes(type.split('/')[1].toLowerCase())) {
    ElMessage.error(`格式不正确, 请上传${fileType.join('/')}格式!`)
    return false
  }
  if (size / 1024 / 1024 > maxSize) {
    ElMessage.error(`大小不能超过${maxSize}MB!`)
    return false
  }
  if (props.aspectRatio) {
    await verificationPicPx(file)
  }
  number.value++
  fileList.value.push({ uid: file.uid, loading: true })
  ossPut(file)
  return false
}
// 截取文件名中的扩展名
function getExtension(filename) {
  const match = filename.match(/\.([^.]+)$/)
  return match ? match[1] : ''
}
const generateUniqueFileName = (fileName) => {
  const index = fileName.lastIndexOf('.');
  return `${fileName.substring(0, index)}_${new Date().getTime()}${fileName.substring(index)}`;
};
async function ossPut(file) {
  try {
    // 填写OSS文件完整路径和本地文件的完整路径。OSS文件完整路径中不能包含Bucket名称。
    // 如果本地文件的完整路径中未指定本地路径，则默认从示例程序所属项目对应本地路径中上传文件。
    const fileName = generateUniqueFileName(file.name);
    const data = await cosObj.uploadFile({
      Bucket: 'yj-mail-1307961934', // 填写自己的 bucket，必须字段
      Region: 'ap-shanghai', // 存储桶所在地域，必须字段
      Key: fileName, // 存储在桶里的对象键（例如1.jpg，a/b/test.txt），必须字段
      Body: file, // 上传文件对象
      SliceSize: 1024 * 1024 * 5, // 触发分块上传的阈值，超过5MB 使用分块上传，小于5MB使用简单上传。可自行设置，非必须
    })
    handleUploadSuccess(data, file)
  }
  catch (e) {
    window.console.log(e)
  }
}
// 图片比例校验
function verificationPicPx(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader()
    reader.readAsDataURL(file)
    reader.onload = (e) => {
      const img = new Image()
      img.src = e.target?.result
      img.onload = () => {
        const { width, height } = img
        const ratio = width / height
        if (ratio !== props.aspectRatio) {
          ElMessage.error(`图片宽高比不符合要求！`)
          reject(new Error(`图片宽高比不符合要求！`))
        }
        else {
          resolve(true)
        }
      }
    }
  })
}

// 上传成功回调
function handleUploadSuccess(result, file) {
  if (result?.statusCode === 200) {
    fileList.value = fileList.value.map((item) => {
      if (item.uid === file.uid) {
        item.url = result.Location.includes('https://') ? result.Location : `https://${result.Location}`
        item.name = file.name
        delete item.loading
        delete item.uid
        uploadList.value.push(item)
      }
      return item
    })
    emit('update:modelValue', fileList.value)
    uploadedSuccessfully()
  }
  else {
    number.value--
    ElMessage.error(res.msg)
    imageUploadRef.value?.handleRemove(file)
    uploadedSuccessfully()
  }
}
// 上传结束处理
function uploadedSuccessfully() {
  if (number.value > 0 && uploadList.value.length === number.value) {
    uploadList.value = []
    number.value = 0
  }
}

// 文件个数超出
function handleExceed() {
  ElMessage.error(`上传文件数量不能超过 ${props.limit} 个!`)
}

// 上传失败
function handleUploadError() {
  ElMessage.error('上传失败')
}

// 删除图片
function handleDelete(index) {
  fileList.value.splice(index, 1)
  emit('update:modelValue', fileList.value)
}

function handleDownLoad(index) {
  const url = fileList.value[index].url
  const suffix = url.slice(url.lastIndexOf('.'))
  const filename = Date.now() + suffix
  fetch(url)
    .then((response) => response.blob())
    .then((blob) => {
      const blobUrl = URL.createObjectURL(new Blob([blob]))
      const link = document.createElement('a')
      link.href = blobUrl
      link.download = filename
      document.body.appendChild(link)
      link.click()
      URL.revokeObjectURL(blobUrl)
      link.remove()
    })
}

// const handleDelete = (file: UploadFile): boolean => {
//   const findex = fileList.value.map((f) => f.name).indexOf(file.name)
//   console.log(findex)
//   if (findex > -1 && uploadList.value.length === number.value) {
//     fileList.value.splice(findex, 1)
//     emit('update:modelValue', fileList)
//     return false
//   }
//   return true
// }

function handlePictureCardPreview(uploadFile) {
  dialogImageUrl.value = uploadFile.url
  dialogVisible.value = true
}

function initSortable() {
  const el = fileListRef.value
  const sortableObj = new Sortable(el, {
    filter: '.ignore-elements',
    onMove: (evt) => {
      return evt.related.firstElementChild?.className !== 'ignore-elements'
    },
    onEnd: ({ oldIndex, newIndex }) => {
      if (oldIndex !== undefined && newIndex !== undefined) {
        fileList.value.splice(newIndex, 0, fileList.value.splice(oldIndex, 1)[0])
        const newArray = fileList.value.slice(0)
        fileList.value = []
        nextTick(() => {
          fileList.value = newArray
          emit('update:modelValue', fileList.value)
        })
      }
    },
  })
  window.console.log(sortableObj)
}

onMounted(() => {
  if (!props.disabled) {
    initSortable()
  }
})
</script>

<template>
  <div>
    <div ref="fileListRef" class="fileList">
      <div
        v-for="(item, key) in fileList"
        :key="key"
        v-loading="item.loading"
        class="img-item"
      >
        <template v-if="item.url">
          <img v-if="isValidImageUrl(item.url)" :src="item.url" class="thumb">
          <Document v-else class="thumb" style="color: #3B7AEE;" />
        </template>
        <div class="menu">
          <ZoomIn v-if="isValidImageUrl(item.url)" class="icons" @click="handlePictureCardPreview(item)"></ZoomIn>
          <Download class="icons" @click="handleDownLoad(key)"></Download>
          <Delete v-if="!disabled" class="icons" @click="handleDelete(key)"></Delete>
        </div>
      </div>
      <el-upload
        v-if="!disabled"
        ref="imageUploadRef"
        drag
        :class="{ hide: fileList.length >= limit }"
        multiple
        :show-file-list="false"
        action=""
        :limit="limit"
        :file-list="fileList"
        :accept="accept"
        list-type="picture-card"
        :on-error="handleUploadError"
        :on-exceed="handleExceed"
        :before-upload="handleBeforeUpload"
        class="ignore-elements"
      >
        <i class="iconfont icon-plus"></i>
      </el-upload>
    </div>
    <!-- 上传提示 -->
    <div v-if="showTip" class="el-upload__tip">
      <template v-if="maxSize">
        大小不超过<b style="color: #f56c6c;">{{ maxSize }}MB</b>
      </template>
      <template v-if="fileType?.length">
        ，格式为<b style="color: #f56c6c;">{{ fileType.join('/') }}</b>
      </template>
      <template v-if="limit > 1">
        ，最多上传<b style="color: #f56c6c;">{{ limit }}张</b>
      </template>
      <template v-if="aspectRatio">
        ，宽高比 <b style="color: #f56c6c;">{{ aspectRatioStr }}</b>
      </template>
    </div>
    <el-dialog v-model="dialogVisible" :show-close="false" :close-on-click-modal="true">
      <img :src="dialogImageUrl" alt="Preview Image" style="max-width: 100%">
    </el-dialog>
  </div>
</template>

<style scoped lang="scss">
// .el-upload--picture-card 控制加号部分
:deep(.hide .el-upload--picture-card) {
  display: none;
}

:deep(.el-upload-list--picture-card .el-upload-list__item) {
  width: 100px;
  height: 100px;
}

:deep(.el-upload--picture-card) {
  width: 100px;
  height: 100px;
  line-height: 100px;
  border: 0,
}
:deep(.el-upload-dragger) {
  padding: 0;
  width: 100%;
  height: 100%;
}
.fileList {
  display: flex;
  flex-wrap: wrap;
}
.img-item {
  border: 1px solid #eee;
  position: relative;
  width: 100px;
  height: 100px;
  overflow: hidden;
  border-radius: 6px;
  margin-right: 8px;
  margin-bottom: 8px;
  .thumb {
    width: 100%;
    height: 100%;
    object-fit: contain;
  }
  .menu {
    position: absolute;
    left: 0;
    top: 0;
    height: 100%;
    width: 100%;
    justify-content: center;
    background-color: var(--el-overlay-color-lighter);
    opacity: 0;
    transition-property: opacity;
    transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
    transition-duration: 150ms;
    display: flex;
    align-items: center;
    justify-content: center;
    .icons {
      cursor: pointer;
      color: #fff;
      width: 20px;
      & + .icons {
        margin-left: 16px;
      }
    }
    &:hover {
      opacity: 1;
    }
  }
}
</style>
